from expression import expression
from expression import environment
from functionDef import functionDef
from customExceptions import ReturnExcpt, GeneralException
from endvals import void
import sys
sys.path.append(sys.path[0] + "/../LLVM")
from llvmhelper import llvmHelper
from llvm import *
from commonRoutines import commonRoutines


class application(expression):
	def __init__(self, name, args):
		self.args = []
		self.name = name
		self.args = args
		self.eFrame = None
		
	#applies closures
	def evaluate(self, env):
		
		function = env.getVarRef(self.name)
		print "\nApp: ", self.name, " called\n",
		params = function.getParams()
		print "params: ", params, "\n",
		print "args: ", self.args, "\n",
		
		#append arguments, with correct bindings, to new enviroment
		#call function body
		if(len(params) == 1 and params[0] == None):
				params = []
		
		if(len(self.args) != len(params)):
			raise GeneralException("Function call to Function " + str(self.name) + " Has " + str(len(self.args)) + " args expected "+ str(len(params)))
		else:
			newEnv = function.getEnv()
			#print "arg Values:",
			for arg, param in zip(self.args, params):
				# I think this line should do this:
				# for each parameter in the function, add a variable('x') to the envrionment
				# 'x' has the value of the associated argument, passed in the application
				# In addition, the new environment ('newEnv') is updated as params are added
				# but the old env ('env') is not updated - not that all the argument evaluations
				# use 'env'. I think this is the way to do it.
				thisArg = arg.evaluate(env)
			#	print param, "(", thisArg, "),",
				newEnv = environment(param, thisArg, newEnv) 
			#print "\n",
			try:
				function.evaluate(newEnv)
			except ReturnExcpt, ret:
				return ret.getValue()

	def toLLVM(self, helper):
		helper.descend("application: " + self.name)
		
		for a in self.args:
			a.toLLVM(helper)
			
		c = commonRoutines(helper)
		
		mallocReg = helper.getNextReg()
		argArrayReg = helper.getNextReg()
		pointerToNumReg = helper.getNextReg()
		resultReg = helper.getNextReg()
		
		helper.addLLVMLine("; Calling " + self.name)
		
		helper.addLLVMInst(llvm_malloc(mallocReg, "{i32, [ " + str(len(self.args)) + " x i32 ]}"), "Creating array for args")
		helper.addLLVMInst(llvm_bitcast(argArrayReg, mallocReg, "{i32, [ " + str(len(self.args)) + " x i32 ]}*", "%valArray*"), "Bitcasting to the right type")
		helper.addLLVMInst(llvm_gep(pointerToNumReg, argArrayReg, "%valArray*", 0, "i32", 0), "Get location of array size")
		helper.addLLVMInst(llvm_store(len(self.args), pointerToNumReg), "Thow in number (not shifted!)")
	
		argValues = helper.getReturns()
	
		for a in range(len(self.args)):
			argAddr = helper.getNextReg()
			
			helper.addLLVMInst(llvm_gep(argAddr, argArrayReg, "%valArray*", 0, "i32", 1, "i32", a), "Get location of " + str(a) + "th argument")
			helper.addLLVMInst(llvm_store(argValues[a], argAddr), "Good god, I hope those are I32s. Adding arg")
		
		
		helper.setLabel(helper.getLastLabel())
		#sys.stderr.write("IN APP " + self.name + ": eframe = " + str(self.eframe)+ "\n")
		#sys.stderr.write("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n") 
		if(self.eframe != None):
			funcClosure = c.fetchVar(self.eframe[1], self.eframe[2])
		else:
			funcClosure = helper.checkCurrentFunctionForArg(self.name)
			#sys.stderr.write("No eframe! " + str(funcClosure) + "\n")

		
		#sys.stderr.write("IN Same APP " + self.name + ": funcClosure = " + str(funcClosure) + "\n")
		
		helper.addLLVMInst(llvm_call("dispatch", "i32", resultReg, funcClosure, "i32", argArrayReg, "%valArray*"), "Call Dispatch!")
		
		helper.setLabel(helper.getLastLabel())
		helper.ascend(resultReg)
		

	def __str__(self):
		retval = ""
		retval = "app (" + str(self.name) + " ( "
		for arg in self.args:
			retval += str(arg) + ", "
		retval = retval[0:-2]
		retval += ") )"
		return retval
	def translate(self):
			if(self.name == '+'):
					return 'add i32 '+ self.args[0].translate() + ', ' + self.args[1].translate() + ';'
			elif(self.name == '-'):
					return 'sub i32 '+ self.args[0].translate() + ', ' + self.args[1].translate() + ';'
			elif(self.name == '*'):
					return 'mul i32 '+ self.args[0].translate() + ', ' + self.args[1].translate() + ';'
			elif(self.name == '/'):
					return 'udiv i32 '+ self.args[0].translate() + ', ' + self.args[1].translate() + ';'

	def traverse(self, helper):
		scope = helper.getCurrentScope()
#		sys.stderr.write("\nvarRef: scope: " + str(scope))
		#var = helper.getVar(self.name,scope)
		#sys.stderr.write("\nvarRef: self.eframe in APP: " + str(var))
#		self.eframe = var
		self.eframe = helper.getVar(self.name,scope)
		#sys.stderr.write("\nvarRef: self.eframe in APP " + self.name +": " + str(self.eframe) + "\n")
		
		for arg in self.args:
			#print "Traversing app "+ self.name + " arg: "+ str(arg) +"\n"
			arg.traverse(helper)
		
	
		


